var gamejs = require("gamejs");
var matchModule = require("js/match");
var enemyModule = require("js/enemy");
var turretModule = require("js/turret");

/**
 * Creates a simple bullet, it draws an half-line starting from the turret
 * and passing by the central point of the target. If the target isn't hit,
 * the bullet continues its linear path.
 * 
 * @param {Match} match The Match object where this object will work.
 * @param {Turret} turret The Turret object who generated this bullet.
 * @param {Enemy} target The Enemy object against which this bullet
 * is fired.
 * @throws {TypeError} If match isn't a Match object, turret isn't a Turret object
 * or target isn't a Enemy object.
 * 
 * @constructor
 */
var Bullet = exports.Bullet = function(match, turret, target) {
	if( ! (match instanceof matchModule.Match)) {
		throw new TypeError("match isn't a valid Match object");
	}
	
	if( ! (turret instanceof turretModule.Turret)) {
		throw new TypeError("turret isn't a valid Turret object");
	}
	
	if( ! (target instanceof enemyModule.Enemy)) {
		throw new TypeError("target isn't a valid Enemy object");
	}
	
	Bullet.superConstructor.apply(this, arguments);
	
	this.upgradeList = [
		{ power : 50, speed : 60}, //level 0
		{ power : 70, speed : 70} //level 1
	];
	
	this.turret = turret;
	
	//takes the level for this bullet from its turret
	this.level = this.turret.getLevel();
	
	//if turret level is higher than the max level of this bullet,
	//takes the maximum level
	if(this.level >= this.upgradeList.length) {
		this.level = this.upgradeList.length - 1;
	}
	
	this.currentMatch = match;
	this.target = target;
	
	//creates the path that this bullet will follow
	this.path = findPath(match.getMap(), turret.getCenter(), target.getCenter());
	this.pathIndex = 0;
};

/**
 * Bullet extend Sprite.
 */
gamejs.utils.objects.extend(Bullet, gamejs.sprite.Sprite);

/**
 * Returns the match where this bullet work.
 *
 * @return {Match} The Match object where this bullet work.
 */
Bullet.prototype.getMatch = function() {
        return this.currentMatch;
};

/**
 * Returns the turret who generated this bullet.
 *
 * @return {Turret} The Turret object who generated this bullet.
 */
Bullet.prototype.getTurret = function() {
        return this.turret;
};

/**
 * Finds and returns an half-line path starting from the sp point given
 * and passing by the tp point given.
 * 
 * @param {Map} map A Map object representing the map to cross.
 * @param {[number, number]} sp Coordinates of the point from where the bullet
 * starts, in [x, y] format.
 * @param {[number, number]} tp Coordinates of the point to hit, in [x, y] format.
 * @return {Array} An array of coordinates in [x, y] format from sp to the
 * boundary of the map.
 */
var findPath = function(map, sp, tp) {
	var tmpPath = [];
	var actualX = sp[X];
	var actualY = sp[Y];
	var deltaVariable;
	
	//use
	//y = m*x + q
	//or
	//x = m'*y + q'
	var m = (tp[Y] - sp[Y]) / (tp[X] - sp[X]);
	var alfa = Math.atan(m);
	
	if((alfa >= - Math.PI / 4) && (alfa <= Math.PI)) {
		//small angle, change the X coordinate
		//and then calculate the Y coordinate
		var q = sp[Y] - (m * sp[X]);
		if(tp[X] > sp[X]) {
			deltaVariable = 1;//the X coordinate must be incremented
		}
		else {
			deltaVariable = -1;//the X coordinate must be decremented
		}
		
		do {
			tmpPath.push([actualX, actualY]);
			actualX += deltaVariable;
			actualY = Math.round(m * actualX + q);
		} while(map.isInside([actualX, actualY]));
	}
	else {
		//big angle, change the Y coordinate
		//and then calculate the X coordinate
		m = 1 / m;
		var q = sp[X] - (m * sp[Y]);
		if(tp[Y] > sp[Y]) {
			deltaVariable = 1;//the Y coordinate must be incremented
		}
		else {
			deltaVariable = -1;//the Y coordinate must be decremented
		}
		
		do {
			tmpPath.push([actualX, actualY]);
			actualY += deltaVariable;
			actualX = Math.round(m * actualY + q);
		} while(map.isInside([actualX, actualY]));
	}
	
	return tmpPath;
};

/**
 * Updates the position of the bullet and checks for collisions.
 *
 * @param {number} msDuration The time past from the last call, in ms.
 */
Bullet.prototype.update = function(msDuration) {
	//updates the position
	this.pathIndex += this.upgradeList[this.level].speed * (msDuration / 1000);
	var intIndex = Math.round(this.pathIndex);
	if(intIndex >= this.path.length) {
		this.kill();//this bullet is going out of the map, kill it
		return;//no more to do here
	}
	
	//checks for collision
	var enemies = this.currentMatch.getMap().getEnemies();
	var intIndex = Math.round(this.pathIndex);
	var collidingEnemies = enemies.collidePoint(this.path[intIndex]);
	var actualPower = this.upgradeList[this.level].power;
	collidingEnemies.forEach(function(currentEnemy) {
		currentEnemy.hit(actualPower);//hit the enemy
		this.kill();//the bullet is exploded
	}, this);
};

/**
 * Draws the bullet in the given Surface.
 *
 * @param {gamejs.Surface} surface The Surface where draw on.
 */
Bullet.prototype.draw = function(surface) {
	var intIndex = Math.round(this.pathIndex);
	var startPoint = intIndex >= 5 ? this.path[intIndex - 5] : this.path[0];
	var endPoint = intIndex + 5 < this.path.length ? this.path[intIndex + 5] : this.path[this.path.length - 1];
	
	gamejs.draw.line(surface, "#FC0FC0", startPoint, endPoint, 3);
};